package dk.itu.mario.level;

import java.util.Random;

import dk.itu.mario.MarioInterface.Constraints;
import dk.itu.mario.MarioInterface.GamePlay;
import dk.itu.mario.MarioInterface.LevelInterface;
import dk.itu.mario.engine.sprites.SpriteTemplate;
import dk.itu.mario.engine.sprites.Enemy;
import dk.itu.mario.engine.LevelFactory;

public class MyCustomizedLevel extends Level implements LevelInterface {

    Random random;

    private static final int ODDS_STRAIGHT = 0;
    private static final int ODDS_HILL_STRAIGHT = 1;
    private static final int ODDS_TUBES = 2;
    private static final int ODDS_JUMP = 3;
    private static final int ODDS_CANNONS = 4;
    private static final int ODDS_CUSTOM = 5;
//    private static final int JumpingThreshold = 3;
    
    private int[] odds = new int[5];
    private int totalOdds;
    
    private int difficulty;
    private int type;

    private int turtles;
    private int coins;
    
    // Tracking # of pieces built
    private int gaps;
    private int straight_piece;
    private int straight_hill_piece;
    private int tube_piece;
    private int cannon_piece;
    private int custom_piece;
    
    private int ENEMIES;
    private int COINS;
    private int BLOCKS_COINS;
    private int BLOCKS_POWER;
    
    private GamePlay playerM;
	enum PlayStyle { COLLECTOR, RUNNER, JUMPER, KILLER }
    private PlayStyle playStyle;
	int skillLvl;

    public MyCustomizedLevel(int width, int height, long seed, int difficulty,
                           int type, GamePlay playerMetrics) {
        super(width, height);
        this.playerM = playerMetrics;
        
        evaluatePlayer();
        
        create(seed, difficulty, type);
        
        System.out.println(">>>>>>>>>>>>>>>LEVEL GENERATED<<<<<<<<<<<<<<<");
        System.out.println("Straight Pieces: "  + straight_piece);
        System.out.println("Straight Hill Pieces: "  + straight_hill_piece);
        System.out.println("Tube Pieces: "  + tube_piece);
        System.out.println("Cannon Pieces: "  + cannon_piece);
        System.out.println("Custom Pieces: "  + custom_piece);
        System.out.println("---------------------------------------");
        System.out.println("Enemies placed: " + ENEMIES);
        System.out.println("Coins placed: " + COINS);
        System.out.println("Coin Blocks placed: " + BLOCKS_COINS);
        System.out.println("Powerup Blocks placed: " + BLOCKS_POWER);
    }

    private void evaluatePlayer() {
		
    	//1-3, higher is more skill (less deaths)
    	skillLvl = 3 - (int)(playerM.timesOfDeathByArmoredTurtle+playerM.timesOfDeathByCannonBall+playerM.timesOfDeathByChompFlower+playerM.timesOfDeathByFallingIntoGap+playerM.timesOfDeathByGoomba+playerM.timesOfDeathByGreenTurtle+playerM.timesOfDeathByJumpFlower+playerM.timesOfDeathByRedTurtle);
    	
    	double totalKills = (playerM.CannonBallKilled + playerM.ArmoredTurtlesKilled + playerM.ChompFlowersKilled + playerM.GoombasKilled + playerM.GreenTurtlesKilled + playerM.RedTurtlesKilled);
    	
    	//Normalize player metrics and score
    	//Coins (COLLECTOR)
    	double coinScore = 90 * ((double) (playerM.coinsCollected + playerM.coinBlocksDestroyed) / (double) (playerM.totalCoins + playerM.totalCoinBlocks));
    	
    	//Running (RUNNER)
        double percentRunning = (double) playerM.timeSpentRunning / (double) playerM.totalTime;
        double percentForward = (double) playerM.timeRunningRight / (double) playerM.totalTime;
        double speedScore = (50 * percentRunning) + (40 * percentForward);
    	
    	//Kills (KILLER)
    	double percentKilled = totalKills / (double) playerM.totalEnemies;
    	double fireKills = (double) playerM.enemyKillByFire / (double) playerM.totalEnemies;
    	double shellKills = (double) playerM.enemyKillByKickingShell / (double) playerM.totalEnemies;
    	double killScore = (70 * percentKilled) + (15 * fireKills) + (15 * shellKills);
    	
    	//Jumps (JUMPER)
    	
    	double percentJumps = playerM.aimlessJumps / (double) playerM.jumpsNumber;
    	double jumpScore = 90 * percentJumps;
    	
    	System.out.println(">>>>>PLAYER EVALUATION SCORES<<<<<");
    	System.out.println("speedScore = 50 * " + percentRunning + " + 40 * " + percentForward +" = " + speedScore);
    	System.out.println("killScore = 70 * " + percentKilled + " + 15 * " + fireKills + " + 15 * " + shellKills + " = " + killScore);
    	System.out.println("jumpScore:  " + jumpScore);
    	System.out.println("coinScore:  " + coinScore);
    	System.out.println("Skill: " + skillLvl);
        System.out.println("-----------------------------------");

    	double playerScore = Math.max(coinScore,(Math.max(speedScore, Math.max(killScore, jumpScore))));
    	
    	if (playerScore == speedScore) {
    		System.out.println("Player evaluated as Runner.\n");
    		playStyle = PlayStyle.RUNNER;
    	}
    	else if (playerScore == coinScore) {
    		System.out.println("Player evaluated as Collector.\n");
    		playStyle = PlayStyle.COLLECTOR;
    	}
    	else if (playerScore == killScore) {
    		System.out.println("Player evaluated as Killer.\n");
    		playStyle = PlayStyle.KILLER;
    	}
    	else {
    		System.out.println("Player evaluated as Jumper.\n");
    		playStyle = PlayStyle.JUMPER;
    	}
    }

	public void create(long seed, int difficulty, int type) {
        this.type = type;
        this.difficulty = difficulty;
        random = new Random(seed);
        
        //Manually set playstyle for testing
//        playStyle = PlayStyle.JUMPER;
        
        switch (playStyle) {
        	case RUNNER: buildRunnerLevel(); break;
        	case COLLECTOR: buildCollectorLevel(); break;
        	case KILLER: buildKillerLevel(); break;
        	case JUMPER: buildJumperLevel(); break;
        }

        fixWalls();

    }

//    private int buildZone(int x, int maxLength) {
//        int t = random.nextInt(totalOdds);
//        int type = 0;
//
//        for (int i = 0; i < odds.length; i++) {
//            if(odds[ODDS_JUMP] <= t*2+30){
//            	type = ODDS_JUMP;
//            	break;
//        }
//        	if (odds[i] <= t) {
//                type = i;
//            }
//        }
//
//        switch (type) {
//        case ODDS_STRAIGHT:
//            return buildStraight(x, maxLength, false);
//        case ODDS_HILL_STRAIGHT:
//            return buildHillStraight(x, maxLength);
//        case ODDS_TUBES:
//            return buildTubes(x, maxLength);
//        case ODDS_JUMP:
//            if (gaps < Constraints.gaps)
//                return buildJump(x, maxLength);
//            else
//                return buildStraight(x, maxLength, false);
//        case ODDS_CANNONS:
//            return buildCannons(x, maxLength);
//        }
//        return 0;
//    }

    private int buildJump(int xo, int maxLength) {
    	System.out.println("Building Jump at " + xo);
    	
        gaps++;
        //jl: jump length
        //js: the number of blocks that are available at either side for free
        int js = random.nextInt(4) + 2;
        int jl = random.nextInt(2) + 2;
        int length = js * 2 + jl;

        boolean hasStairs = random.nextInt(3) == 0;

        int floor = height - 1 - random.nextInt(4);
        
        //Added for killer type levels
        if (playStyle == PlayStyle.KILLER) {
			addEnemyLine(xo + 1, xo + js - 1, floor - 1);
			addEnemyLine(xo +(js*2)+1, xo + (js*2) + jl -1, floor - 1);
        }
        
        //run for the from the start x position, for the whole length
        for (int x = xo; x < xo + length; x++) {
            if (x < xo + js || x > xo + length - js - 1) {
                //run for all y's since we need to paint blocks upward
                for (int y = 0; y < height; y++) { //paint ground up until the floor
                    if (y >= floor) {
                        setBlock(x, y, GROUND);
                    }
                    //if it is above ground, start making stairs of rocks
                    else if (hasStairs) { //LEFT SIDE
                        if (x < xo + js) { //we need to max it out and level because it wont
                            //paint ground correctly unless two bricks are side by side
                            if (y >= floor - (x - xo) + 1) {
                                setBlock(x, y, ROCK);
                            }
                        } else { //RIGHT SIDE
                            if (y >= floor - ((xo + length) - x) + 2) {
                                setBlock(x, y, ROCK);
                            }
                        }
                    }
                }
            }
        }

        return length;
    }

    private int buildCannons(int xo, int maxLength) {
    	System.out.println("Building Cannons at " + xo);
    	cannon_piece++;
    	
        int length = random.nextInt(10) + 2;
        if (length > maxLength) length = maxLength;

        int floor = height - 1 - random.nextInt(4);
        
        //Added for killer type levels
        if (playStyle == PlayStyle.KILLER) {
			addEnemyLine(xo + 1, xo + length - 1, floor - 6);
        }
        
        int xCannon = xo + 1 + random.nextInt(4);
        for (int x = xo; x < xo + length; x++) {
            if (x > xCannon) {
                xCannon += 2 + random.nextInt(4);
            }
            if (xCannon == xo + length - 1) xCannon += 10;
            int cannonHeight = floor - random.nextInt(4) - 1;

            for (int y = 0; y < height; y++) {
                if (y >= floor) {
                    setBlock(x, y, (byte) (1 + 9 * 16));
                } else {
                    if (x == xCannon && y >= cannonHeight) {
                        if (y == cannonHeight) {
                            setBlock(x, y, (byte) (14 + 0 * 16));
                        } else if (y == cannonHeight + 1) {
                            setBlock(x, y, (byte) (14 + 1 * 16));
                        } else {
                            setBlock(x, y, (byte) (14 + 2 * 16));
                        }
                    }
                }
            }
        }

        return length;
    }

    private int buildHillStraight(int xo, int maxLength) {
    	System.out.println("Building Hill at " + xo);
    	straight_hill_piece++;
    	
        int length = random.nextInt(10) + 10;
        if (length > maxLength) length = maxLength;

        int floor = height - 1 - random.nextInt(4);
        for (int x = xo; x < xo + length; x++) {
            for (int y = 0; y < height; y++) {
                if (y >= floor) {
                    setBlock(x, y,Level.GROUND);
                }
            }
        }

        if (playStyle == PlayStyle.KILLER) {
        	addEnemyLine(xo + 1, xo + length - 1, floor - 1);
        }

        int h = floor;

        boolean keepGoing = true;

        boolean[] occupied = new boolean[length];
        while (keepGoing) {
            h = h - 2 - random.nextInt(3);

            if (h <= 0) {
                keepGoing = false;
            } else {
                int l = random.nextInt(5) + 3;
                int xxo = random.nextInt(length - l - 2) + xo + 1;

                if (occupied[xxo - xo] || occupied[xxo - xo + l] ||
                    occupied[xxo - xo - 1] || occupied[xxo - xo + l + 1]) {
                    keepGoing = false;
                } else {
                    occupied[xxo - xo] = true;
                    occupied[xxo - xo + l] = true;
                    addEnemyLine(xxo, xxo + l, h - 1);
                    if (playStyle == PlayStyle.COLLECTOR) {
                    	decorateCollector(xxo - 1, xxo + l + 1, h);
                    } else if (random.nextInt(4) == 0) {
                            decorate(xxo - 1, xxo + l + 1, h);
                    	}
                    keepGoing = false;
                    for (int x = xxo; x < xxo + l; x++) {
                        for (int y = h; y < floor; y++) {
                            int xx = 5;
                            if (x == xxo) xx = 4;
                            if (x == xxo + l - 1) xx = 6;
                            int yy = 9;
                            if (y == h) yy = 8;

                            if (getBlock(x, y) == 0) {
                                setBlock(x, y, (byte) (xx + yy * 16));
                            } else {
                                if (getBlock(x, y) == Level.HILL_TOP_LEFT)
                                    setBlock(x, y, Level.HILL_TOP_LEFT_IN);
                                if (getBlock(x, y) == Level.HILL_TOP_RIGHT)
                                    setBlock(x, y, Level.HILL_TOP_RIGHT_IN);
                            }
                        }
                    }
                }
            }
        }

        return length;
    }

    private void addEnemyLine(int x0, int x1, int y) {
        for (int x = x0; x < x1; x++) {
        	//Modified by skillLvl now
            if (random.nextInt(75) < 5*skillLvl) {
            	
                int type = random.nextInt(4);

                    type = random.nextInt(3);
                if (turtles < Constraints.turtles) {
                    if (type == Enemy.ENEMY_GREEN_KOOPA ||
                        type == Enemy.ENEMY_RED_KOOPA) {
                        turtles++;
                        setSpriteTemplate(x, y, new SpriteTemplate(type, random.nextInt(35) < difficulty));
                    } else {
                        setSpriteTemplate(x, y, new SpriteTemplate(type, random.nextInt(35) < difficulty));
                    }
                }
                else{
                	setSpriteTemplate(x, y, new SpriteTemplate(Enemy.ENEMY_GOOMBA, random.nextInt(35) < difficulty));
                }
                ENEMIES++;
            }
        }
    }

    private int buildTubes(int xo, int maxLength) {
    	System.out.println("Building Tubes at " + xo);
    	tube_piece++;
    	
        int length = random.nextInt(10) + 5;
        if (length > maxLength) length = maxLength;

        int floor = height - 1 - random.nextInt(4);
        int xTube = xo + 1 + random.nextInt(4);
        int tubeHeight = floor - random.nextInt(2) - 2;
        
        //Added for killer type levels
        if (playStyle == PlayStyle.KILLER) {
			addEnemyLine(xo + 1, xo + length - 1, floor - 6);
        }
        
        for (int x = xo; x < xo + length; x++) {
            if (x > xTube + 1) {
                xTube += 3 + random.nextInt(4);
                tubeHeight = floor - random.nextInt(2) - 2;
            }
            if (xTube >= xo + length - 2) xTube += 10;

            if (x == xTube && random.nextInt(11) < difficulty + 1) {
                setSpriteTemplate(x, tubeHeight, new SpriteTemplate(Enemy.ENEMY_FLOWER, false));
            }

            for (int y = 0; y < height; y++) {
                if (y >= floor) {
                    setBlock(x, y, (byte) (1 + 9 * 16));

                } else {
                    if ((x == xTube || x == xTube + 1) && y >= tubeHeight) {
                        int xPic = 10 + x - xTube;

                        if (y == tubeHeight) {
                            //tube top
                            setBlock(x, y, (byte) (xPic + 0 * 16));
                        } else {
                            //tube side
                            setBlock(x, y, (byte) (xPic + 1 * 16));
                        }
                    }
                }
            }
        }

        return length;
    }

    private int buildStraight(int xo, int maxLength, boolean safe) {
    	System.out.println("Building Straight at " + xo);
    	straight_piece++;
    	
        int length = random.nextInt(10) + 2;

        if (safe)
            length = 10 + random.nextInt(5);

        if (length > maxLength)
            length = maxLength;

        int floor = height - 1 - random.nextInt(4);

        //Added for killer type level
        if (playStyle == PlayStyle.KILLER && !safe) {
        	for (int i=0; i < skillLvl; i++) {
        		addEnemyLine(xo + 1, xo + length - 1, floor - 1 - i);
        	}
        }
        
        //runs from the specified x position to the length of the segment
        for (int x = xo; x < xo + length; x++) {
            for (int y = 0; y < height; y++) {
                if (y >= floor) {
                    setBlock(x, y, Level.GROUND);
                }
            }
        }

        if (!safe) {
            if (length > 5) {
            	//Added for collector type level
            	if (playStyle == PlayStyle.COLLECTOR) {
            		decorateCollector(xo, xo + length, floor);
            	} else {
            		decorate(xo, xo + length, floor);
            	}
            }
        }

        return length;
    }

    private void decorateCollector(int xStart, int xLength, int floor) {
    	System.out.println("Decorating Collector at " + xStart);
    	//if its at the very top, just return
        if (floor < 1)
        	return;
        boolean rocks = true; 

        //add an enemy line above the box
        addEnemyLine(xStart + 1, xLength - 1, floor - 1);

        //Gives longer coin lines
        int coinStart = xStart + 1 + random.nextInt(3);
        int coinEnd = xLength - 1 - random.nextInt(3);
        
        //More coins...may intersect floor though
        for (int i=0; i <= 2; i++) {
        if (floor - 2 - i > 0) {
            if ((coinEnd) - (coinStart) > 1) {
            	for(int x = coinStart; x < coinEnd; x++){
                    setBlock(x, floor - 2 - i, COIN);
                    COINS++;
                }
            }
        }
        }

        //More blocks
        int blockStart = xStart + 1 + random.nextInt(4);
        int blockEnd = xLength -1 - random.nextInt(4);
        
        //this fills the set of blocks and the hidden objects inside them
		for(int i = 0; i <= 3; i++) {
			if (floor - 4 - i > 0) {
				if (blockEnd - blockStart > 2) {
					for (int x = blockStart; x < blockEnd; x++) {
						if (rocks) {
							if (x != xStart + 1 && x != xLength - 1 && i == 0) {
								//Fills question mark blocks
								if (random.nextInt(3) == 0) {
									setBlock(x, floor - 4 - i, BLOCK_POWERUP);
									BLOCKS_POWER++;
								} else if (random.nextInt(2) == 0) {	
									//Fills a block with a hidden coin
									setBlock(x, floor - 4 - i, BLOCK_COIN);
									BLOCKS_COINS++;
								} else {
									setBlock(x, floor - 4 - i, BLOCK_EMPTY);
								}
							} else if (x != xStart + 1 && x != xLength -1) {
								//Fill with coins
								setBlock(x, floor - 4 - i, COIN);
			                    COINS++;
							}
						}
					}
				}
			}
		}	
	}

	private void decorate(int xStart, int xLength, int floor) {
        //if its at the very top, just return
        if (floor < 1)
            return;
        boolean rocks = true;

        //add an enemy line above the box
        addEnemyLine(xStart + 1, xLength - 1, floor - 1);

        int s = random.nextInt(4);
        int e = random.nextInt(4);

        if (floor - 2 > 0) {
            if ((xLength - 1 - e) - (xStart + 1 + s) > 1) {
                for (int x = xStart + 1 + s; x < xLength - 1 - e; x++) {
                    setBlock(x, floor - 2, COIN);
                    COINS++;
                }
            }
        }

        s = random.nextInt(4);
        e = random.nextInt(4);

        if (floor - 4 > 0) {
            if ((xLength - 1 - e) - (xStart + 1 + s) > 2) {
                for (int x = xStart + 1 + s; x < xLength - 1 - e; x++) {
                    if (rocks) {
                        if (x != xStart + 1 && x != xLength - 2 && random.nextInt(2) == 0) {
                            if (random.nextInt(2) == 0) {
                                setBlock(x, floor - 4, BLOCK_POWERUP);
                            } else {
                                if(coins < Constraints.coinBlocks){
                                    coins++;
                                    setBlock(x, floor - 4, BLOCK_COIN);
                                }
                                else{
                                    setBlock(x, floor - 4, BLOCK_EMPTY);
                                }
                            }
                        } else if (random.nextInt(4) == 0) {
                            if (random.nextInt(4) == 0) {
                                setBlock(x, floor - 4, (byte) (2 + 1 * 16));
                            } else {
                                setBlock(x, floor - 4, (byte) (1 + 1 * 16));
                            }
                        } else {
                            setBlock(x, floor - 4, BLOCK_EMPTY);
                        }
                    }
                }
            }
        }
    }

    private void fixWalls() {
        boolean[][] blockMap = new boolean[width + 1][height + 1];

        for (int x = 0; x < width + 1; x++) {
            for (int y = 0; y < height + 1; y++) {
                int blocks = 0;
                for (int xx = x - 1; xx < x + 1; xx++) {
                    for (int yy = y - 1; yy < y + 1; yy++) {
                        if (getBlockCapped(xx, yy) == GROUND) {
                            blocks++;
                        }
                    }
                }
                blockMap[x][y] = blocks == 4;
            }
        }
        blockify(this, blockMap, width + 1, height + 1);
    }

    private void blockify(Level level, boolean[][] blocks, int width,
                          int height) {
        int to = 0;
        if (type == LevelInterface.TYPE_CASTLE) {
            to = 4 * 2;
        } else if (type == LevelInterface.TYPE_UNDERGROUND) {
            to = 4 * 3;
        }

        boolean[][] b = new boolean[2][2];

        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                for (int xx = x; xx <= x + 1; xx++) {
                    for (int yy = y; yy <= y + 1; yy++) {
                        int _xx = xx;
                        int _yy = yy;
                        if (_xx < 0) _xx = 0;
                        if (_yy < 0) _yy = 0;
                        if (_xx > width - 1) _xx = width - 1;
                        if (_yy > height - 1) _yy = height - 1;
                        b[xx - x][yy - y] = blocks[_xx][_yy];
                    }
                }

                if (b[0][0] == b[1][0] && b[0][1] == b[1][1]) {
                    if (b[0][0] == b[0][1]) {
                        if (b[0][0]) {
                            level.setBlock(x, y, (byte) (1 + 9 * 16 + to));
                        } else {
                            // KEEP OLD BLOCK!
                        }
                    } else {
                        if (b[0][0]) {
                            //down grass top?
                            level.setBlock(x, y, (byte) (1 + 10 * 16 + to));
                        } else {
                            //up grass top
                            level.setBlock(x, y, (byte) (1 + 8 * 16 + to));
                        }
                    }
                } else if (b[0][0] == b[0][1] && b[1][0] == b[1][1]) {
                    if (b[0][0]) {
                        //right grass top
                        level.setBlock(x, y, (byte) (2 + 9 * 16 + to));
                    } else {
                        //left grass top
                        level.setBlock(x, y, (byte) (0 + 9 * 16 + to));
                    }
                } else if (b[0][0] == b[1][1] && b[0][1] == b[1][0]) {
                    level.setBlock(x, y, (byte) (1 + 9 * 16 + to));
                } else if (b[0][0] == b[1][0]) {
                    if (b[0][0]) {
                        if (b[0][1]) {
                            level.setBlock(x, y, (byte) (3 + 10 * 16 + to));
                        } else {
                            level.setBlock(x, y, (byte) (3 + 11 * 16 + to));
                        }
                    } else {
                        if (b[0][1]) {
                            //right up grass top
                            level.setBlock(x, y, (byte) (2 + 8 * 16 + to));
                        } else {
                            //left up grass top
                            level.setBlock(x, y, (byte) (0 + 8 * 16 + to));
                        }
                    }
                } else if (b[0][1] == b[1][1]) {
                    if (b[0][1]) {
                        if (b[0][0]) {
                            //left pocket grass
                            level.setBlock(x, y, (byte) (3 + 9 * 16 + to));
                        } else {
                            //right pocket grass
                            level.setBlock(x, y, (byte) (3 + 8 * 16 + to));
                        }
                    } else {
                        if (b[0][0]) {
                            level.setBlock(x, y, (byte) (2 + 10 * 16 + to));
                        } else {
                            level.setBlock(x, y, (byte) (0 + 10 * 16 + to));
                        }
                    }
                } else {
                    level.setBlock(x, y, (byte) (0 + 1 * 16 + to));
                }
            }
        }
    }
    
	  private void buildRunnerLevel() {

		  int difficulty = 5 * skillLvl;

		  int levelLength = width; 
		  //Constrained to 320 for now
		
		  int[] odds = new int[6];
		  //Ignoring difficulty for now, may implement later
		
		  int sectionType = 0;
		
		  //Runner levels have more straight sections, less tubes/cannons/jumps
		  odds[ODDS_STRAIGHT] = 40;
		  odds[ODDS_HILL_STRAIGHT] = 40;
		  odds[ODDS_TUBES] = 1*difficulty;
		  odds[ODDS_JUMP] = 1*difficulty;
		  odds[ODDS_CANNONS] = 1*difficulty;
		  odds[ODDS_CUSTOM] = 2*difficulty;
		
		int rand = random.nextInt(odds[ODDS_CANNONS] + odds[ODDS_JUMP]+odds[ODDS_TUBES]+odds[ODDS_HILL_STRAIGHT]+odds[ODDS_STRAIGHT] + odds[ODDS_CUSTOM]);
	
		//create the start location
		int length = 0;
		length += buildStraight(0, width, true);

		//Custom first for testing
//		length += buildRunnerSpecial(length, levelLength-length, false);
		
	    //create all of the medium sections
	    while (length < levelLength - 64) {
	    	  rand = random.nextInt(odds[ODDS_CANNONS] + odds[ODDS_JUMP]+odds[ODDS_TUBES]+odds[ODDS_HILL_STRAIGHT]+odds[ODDS_STRAIGHT] + odds[ODDS_CUSTOM]);
	    	  sectionType = chooseSectionType(odds,rand);
	    	  switch (sectionType) {
	    	  	case ODDS_STRAIGHT:
					length += buildStraight(length, levelLength-length, false);
					break;
				case ODDS_HILL_STRAIGHT:
					length += buildHillStraight(length, levelLength-length);
					break;
				case ODDS_TUBES:
					length += buildTubes(length, levelLength-length);
					break;
				case ODDS_JUMP:
					length += buildJump(length, levelLength-length);
					break;
				case ODDS_CANNONS:
					length += buildCannons(length, levelLength-length);
					break;
				case ODDS_CUSTOM:
					if (random.nextInt(10) == 0) {
						length += buildJumperSpecial(length, levelLength-length, false);
					} else if (random.nextInt(10) == 0) {
						length += buildKillerSpecial(length, levelLength - length, false);
					} else if (random.nextInt(10) == 0) {
						length += buildCollectorSpecial(length, levelLength - length, false);
					} else {
					length += buildRunnerSpecial(length, levelLength-length, false);
					}
			}
	      }
	      
		  //set the end piece
	      int floor = height - 1 - random.nextInt(4);

	      xExit = length + 8;
	      yExit = floor;

	       // fills the end piece
	       for (int x = length; x < width; x++) {
	           for (int y = 0; y < height; y++) {
	               if (y >= floor) {
	            	   setBlock(x, y, GROUND);
	               }
	           }
	       }

	       if (type == LevelInterface.TYPE_CASTLE || type == LevelInterface.TYPE_UNDERGROUND) {
	            int ceiling = 0;
	            int run = 0;
	            for (int x = 0; x < width; x++) {
	               if (run-- <= 0 && x > 4) {
	                   ceiling = random.nextInt(4);
	                   run = random.nextInt(4) + 4;
	                }
	                for (int y = 0; y < height; y++) {
	                   if ((x > 4 && y <= ceiling) || x < 1) {
	                       setBlock(x, y, GROUND);
	                   }
	               }
	           }
	       }
	  }
	  
	  private void buildCollectorLevel() {
		  int levelLength = width;
		  
		  int difficulty = 5 * skillLvl;
		
		  int[] odds = new int[6];
		
		  int sectionType = 0;
		
		  //Collectors have less straight/hill pieces, more tubes/cannons/custom pieces
		  odds[ODDS_STRAIGHT] = 30;
		  odds[ODDS_HILL_STRAIGHT] = 30;
		  odds[ODDS_TUBES] = 2*difficulty;
		  odds[ODDS_JUMP] = 3*difficulty;
		  odds[ODDS_CANNONS] = 2*difficulty;
		  odds[ODDS_CUSTOM] = 2*difficulty;
		
		  int rand = random.nextInt(odds[ODDS_CANNONS] + odds[ODDS_JUMP]+odds[ODDS_TUBES]+odds[ODDS_HILL_STRAIGHT]+odds[ODDS_STRAIGHT]+odds[ODDS_CUSTOM]);
		
		  //create the start location
		  int length = 0;
		  length += buildStraight(0, width, true);
		
		  //Build custom first for testing
//		  length += buildCollectorSpecial(length, levelLength-length, false);
		  
	      //create all of the medium sections
	      while (length < levelLength - 64) {
	    	  rand = random.nextInt(odds[ODDS_CANNONS] + odds[ODDS_JUMP]+odds[ODDS_TUBES]+odds[ODDS_HILL_STRAIGHT]+odds[ODDS_STRAIGHT]+odds[ODDS_CUSTOM]);
	    	  sectionType = chooseSectionType(odds,rand);
	    	  //sectionType = ODDS_CUSTOM;
	    	  switch (sectionType) {
	    	  	case ODDS_STRAIGHT:
					length += buildStraight(length, levelLength-length, false);
					break;
				case ODDS_HILL_STRAIGHT:
					length += buildHillStraight(length, levelLength-length);
					break;
				case ODDS_TUBES:
					length += buildTubes(length, levelLength-length);
					break;
				case ODDS_JUMP:
					length += buildJump(length, levelLength-length);
					break;
				case ODDS_CANNONS:
					length += buildCannons(length, levelLength-length);
					break;
				case ODDS_CUSTOM:
					if (random.nextInt(10) == 0) {
						length += buildJumperSpecial(length, levelLength-length, false);
					} else if (random.nextInt(10) == 0) {
						length += buildKillerSpecial(length, levelLength - length, false);
					} else if (random.nextInt(10) == 0) {
						length += buildRunnerSpecial(length, levelLength - length, false);
					} else {
						length += buildCollectorSpecial(length, levelLength-length, false);
					}
	    	  }
	      }
	      
	    //set the end piece
	        int floor = height - 1 - random.nextInt(4);

	        xExit = length + 8;
	        yExit = floor;

	        // fills the end piece
	        for (int x = length; x < width; x++)
	        {
	            for (int y = 0; y < height; y++)
	            {
	                if (y >= floor)
	                {
	                    setBlock(x, y, GROUND);
	                }
	            }
	        }

	        if (type == LevelInterface.TYPE_CASTLE || type == LevelInterface.TYPE_UNDERGROUND)
	        {
	            int ceiling = 0;
	            int run = 0;
	            for (int x = 0; x < width; x++)
	            {
	                if (run-- <= 0 && x > 4)
	                {
	                    ceiling = random.nextInt(4);
	                    run = random.nextInt(4) + 4;
	                }
	                for (int y = 0; y < height; y++)
	                {
	                    if ((x > 4 && y <= ceiling) || x < 1)
	                    {
	                        setBlock(x, y, GROUND);
	                    }
	                }
	            }
	        }
	      
	  }
	  
	  private void buildKillerLevel() {
		  
		  int levelLength = width;
		  
		  int difficulty = 5 * skillLvl;
		
		  int[] odds = new int[6];
		
		  int sectionType = 0;
		
		  //Killers have about the same amount straight/hill/tubes but more cannons/custom pieces, less jumps
		  odds[ODDS_STRAIGHT] = 30;
		  odds[ODDS_HILL_STRAIGHT] = 30;
		  odds[ODDS_TUBES] = 1*difficulty;
		  odds[ODDS_JUMP] = 1*difficulty;
		  odds[ODDS_CANNONS] = 3*difficulty;
		  odds[ODDS_CUSTOM] = 2*difficulty;
		
		  int rand = random.nextInt(odds[ODDS_CANNONS] + odds[ODDS_JUMP]+odds[ODDS_TUBES]+odds[ODDS_HILL_STRAIGHT]+odds[ODDS_STRAIGHT]+odds[ODDS_CUSTOM]);
		
		  //create the start location
		  int length = 0;
		  length += buildStraight(0, width, true);
		  
		  //Build custom first for testing
//		  length += buildKillerSpecial(length, levelLength-length, false);
		  
	      //create all of the medium sections
	      while (length < levelLength - 64) {
	    	  rand = random.nextInt(odds[ODDS_CANNONS] + odds[ODDS_JUMP]+odds[ODDS_TUBES]+odds[ODDS_HILL_STRAIGHT]+odds[ODDS_STRAIGHT]+odds[ODDS_CUSTOM]);
	    	  sectionType = chooseSectionType(odds,rand);
	    	  //sectionType = ODDS_CUSTOM;
	    	  switch (sectionType) {
	    	  	case ODDS_STRAIGHT:
					length += buildStraight(length, levelLength-length, false);
					break;
				case ODDS_HILL_STRAIGHT:
					length += buildHillStraight(length, levelLength-length);
					break;
				case ODDS_TUBES:
					length += buildTubes(length, levelLength-length);
					break;
				case ODDS_JUMP:
					length += buildJump(length, levelLength-length);
					break;
				case ODDS_CANNONS:
					length += buildCannons(length, levelLength-length);
					break;
				case ODDS_CUSTOM:
					if (random.nextInt(10) == 0) {
						length += buildJumperSpecial(length, levelLength-length, false);
					} else if (random.nextInt(10) == 0) {
						length += buildCollectorSpecial(length, levelLength - length, false);
					} else if (random.nextInt(10) == 0) {
						length += buildRunnerSpecial(length, levelLength - length, false);
					} else {
					length += buildKillerSpecial(length, levelLength-length, false);
					}
			}
	      }
	      
	    //set the end piece
	        int floor = height - 1 - random.nextInt(4);

	        xExit = length + 8;
	        yExit = floor;

	        // fills the end piece
	        for (int x = length; x < width; x++)
	        {
	            for (int y = 0; y < height; y++)
	            {
	                if (y >= floor)
	                {
	                    setBlock(x, y, GROUND);
	                }
	            }
	        }

	        if (type == LevelInterface.TYPE_CASTLE || type == LevelInterface.TYPE_UNDERGROUND)
	        {
	            int ceiling = 0;
	            int run = 0;
	            for (int x = 0; x < width; x++)
	            {
	                if (run-- <= 0 && x > 4)
	                {
	                    ceiling = random.nextInt(4);
	                    run = random.nextInt(4) + 4;
	                }
	                for (int y = 0; y < height; y++)
	                {
	                    if ((x > 4 && y <= ceiling) || x < 1)
	                    {
	                        setBlock(x, y, GROUND);
	                    }
	                }
	            }
	        }		  
	  }
	  
	  private void buildJumperLevel() {
		  int levelLength = width;
		  
		  int difficulty = 5 * skillLvl;
		
		  int[] odds = new int[6];
		
		  int sectionType = 0;
		
		  //Jumpers have less straight/hill pieces, more tubes/cannons/jumps/custom pieces
		  odds[ODDS_STRAIGHT] = 30;
		  odds[ODDS_HILL_STRAIGHT] = 30;
		  odds[ODDS_TUBES] = 2*difficulty;
		  odds[ODDS_JUMP] = 4*difficulty;
		  odds[ODDS_CANNONS] = 2*difficulty;
		  odds[ODDS_CUSTOM] = 3*difficulty;
		
		  int rand = random.nextInt(odds[ODDS_CANNONS] + odds[ODDS_JUMP]+odds[ODDS_TUBES]+odds[ODDS_HILL_STRAIGHT]+odds[ODDS_STRAIGHT]+odds[ODDS_CUSTOM]);
		
		  //create the start location
		  int length = 0;
		  length += buildStraight(0, width, true);
		
		  //Build custom first for testing
//		  length += buildJumperSpecial(length, levelLength-length, false);
		  
	      //create all of the medium sections
	      while (length < levelLength - 64) {
	    	  rand = random.nextInt(odds[ODDS_CANNONS] + odds[ODDS_JUMP]+odds[ODDS_TUBES]+odds[ODDS_HILL_STRAIGHT]+odds[ODDS_STRAIGHT]+odds[ODDS_CUSTOM]);
	    	  sectionType = chooseSectionType(odds,rand);
	    	  //sectionType = ODDS_CUSTOM;
	    	  switch (sectionType) {
	    	  	case ODDS_STRAIGHT:
					length += buildStraight(length, levelLength-length, false);
					break;
				case ODDS_HILL_STRAIGHT:
					length += buildHillStraight(length, levelLength-length);
					break;
				case ODDS_TUBES:
					length += buildTubes(length, levelLength-length);
					break;
				case ODDS_JUMP:
					length += buildJump(length, levelLength-length);
					break;
				case ODDS_CANNONS:
					length += buildCannons(length, levelLength-length);
					break;
				case ODDS_CUSTOM:
					if (random.nextInt(10) == 0) {
						length += buildCollectorSpecial(length, levelLength-length, false);
					} else if (random.nextInt(10) == 0) {
						length += buildKillerSpecial(length, levelLength - length, false);
					} else if (random.nextInt(10) == 0) {
						length += buildRunnerSpecial(length, levelLength - length, false);
					} else {
					length += buildJumperSpecial(length, levelLength-length, false);
					}
			}
	      }
	      
	    //set the end piece
	        int floor = height - 1 - random.nextInt(4);

	        xExit = length + 8;
	        yExit = floor;

	        // fills the end piece
	        for (int x = length; x < width; x++)
	        {
	            for (int y = 0; y < height; y++)
	            {
	                if (y >= floor)
	                {
	                    setBlock(x, y, GROUND);
	                }
	            }
	        }

	        if (type == LevelInterface.TYPE_CASTLE || type == LevelInterface.TYPE_UNDERGROUND)
	        {
	            int ceiling = 0;
	            int run = 0;
	            for (int x = 0; x < width; x++)
	            {
	                if (run-- <= 0 && x > 4)
	                {
	                    ceiling = random.nextInt(4);
	                    run = random.nextInt(4) + 4;
	                }
	                for (int y = 0; y < height; y++)
	                {
	                    if ((x > 4 && y <= ceiling) || x < 1)
	                    {
	                        setBlock(x, y, GROUND);
	                    }
	                }
	            }
	        }		  
	  }
	  
	  private int chooseSectionType(int[] odds, int rand){
		if(rand < odds[ODDS_STRAIGHT]) {
			return ODDS_STRAIGHT;
		}
		else if(rand < odds[ODDS_HILL_STRAIGHT] + odds[ODDS_STRAIGHT]) {
			return ODDS_HILL_STRAIGHT;
		}
		else if(rand < odds[ODDS_TUBES] + odds[ODDS_HILL_STRAIGHT] + odds[ODDS_STRAIGHT]) {
			return ODDS_TUBES;
		}
		else if(rand < odds[ODDS_JUMP] + odds[ODDS_TUBES] + odds[ODDS_HILL_STRAIGHT] + odds[ODDS_STRAIGHT]) {
			return ODDS_JUMP;
		}
		else if (rand < odds[ODDS_CANNONS] + odds[ODDS_JUMP] + odds[ODDS_TUBES] + odds[ODDS_HILL_STRAIGHT] + odds[ODDS_STRAIGHT]){
			return ODDS_CANNONS;
		} else {
			return ODDS_CUSTOM;
		}
	  }
	  
	  private int buildCollectorSpecial(int xo,  int maxLength, boolean safe) {
	    	System.out.println("Building Custom at " + xo);
	    	custom_piece++;
		  
		  int length = 10 + random.nextInt(5);

	        if (length > maxLength)
	            length = maxLength;

	        int floor = height - 1 - random.nextInt(2);

	        //runs from the specified x position to the length of the segment
	        for (int currentBlockRow = 1; currentBlockRow <= 2; currentBlockRow++) {
	        for (int x = xo; x < xo + length; x++) {
	            
	        	for (int y = 0; y < height; y++) {
	                if (y >= floor) {
	                    setBlock(x, y, Level.GROUND);
	                }
	                else if (floor - (4*currentBlockRow) == y) {
	                	if(x >= xo + 2 && x <= xo + length - 2){
		                	int rand = random.nextInt(100);
		                	int blockType = 1;
		                	if (rand < 50) blockType=0;
		                	if (rand >= 50 && rand < 80) blockType = 1;
		                	if (rand >= 80) blockType = 2;
		                	switch(blockType){
		                	case 0:
		                		setBlock(x,y,BLOCK_EMPTY);
		                		break;
		                	case 1:
		                		setBlock(x,y,BLOCK_COIN);
		                		COINS++;
		                		break;
		                	case 2: 
		                		setBlock(x,y,BLOCK_POWERUP);
		                		BLOCKS_POWER++;
		                		break;
		                	}
	                	}	                	
	                }
	                
	            }
	        }
	        }
	        return length;	    	
	    }
	  
	  
	  private int buildRunnerSpecial(int xo,  int maxLength, boolean safe) {
	    	System.out.println("Building Custom at " + xo);
	    	custom_piece++;
		  
		  int length = 10 + random.nextInt(5);

	        if (length > maxLength)
	            length = maxLength;

	        int floor = height - 1 - random.nextInt(4);

	        //runs from the specified x position to the length of the segment
	        for (int x = xo; x < xo + length; x++) {

	            for (int y = 0; y < height; y++) {
	                
	            	if(x == xo +(length/3) || x == xo +2*(length/3) ) {
	            		if (y == floor) {
	            			setBlock(x,y,TUBE_TOP_LEFT);
	            			setSpriteTemplate(x, y, new SpriteTemplate(SpriteTemplate.JUMP_FLOWER, false));
	            		}
	            		else if(y>=floor) {
	            			setBlock(x,y,TUBE_SIDE_LEFT);
	            		}
	            	}
	            	else if( x== xo+ ((length/3)+1)|| x== xo+ (2*(length/3)+1)) {
	            		if (y==floor){
	            			setBlock(x,y,TUBE_TOP_RIGHT);
	            			//setSpriteTemplate(x, y, new SpriteTemplate(SpriteTemplate.JUMP_FLOWER, false));

	            		}
	            		else if(y>=floor) {
	            			setBlock(x,y,TUBE_SIDE_RIGHT);
	            		}
	            	}
	            	else if (y >= floor) {
	                    setBlock(x, y, Level.GROUND);
	            	}
	            }
	        }
	        return length;	    	
	    }
	  	  
	  private int buildJumperSpecial(int xo,  int maxLength, boolean safe) {
	    	System.out.println("Building Custom at " + xo);
	    	custom_piece++;
		  
		  int length = 10 + random.nextInt(5);

	        if (length > maxLength)
	            length = maxLength;
	        int floor = height - 1 - random.nextInt(4);

	        //runs from the specified x position to the length of the segment
	        for (int x = xo; x < xo + length; x++) {
	            for (int y = 0; y < height; y++) {
	            	if(x <= xo+(length/3)){
	            		if(y == floor-3){
	            			setBlock(x,y,Level.ROCK);
	            		}
	            	}
	            	else if(x> xo+(length/3) && x<=xo + (2*(length/3))){
	            		if(y == floor-6){
	            			setBlock(x,y,Level.ROCK);
	            		}
	            	}
	            	else{
	            		if(x == xo + (2*(length/3))+1){
	            			if(y == floor-6){
		            			setBlock(x,y,Level.HILL_TOP_LEFT);
		            		}
	            			else if(y > floor-6){
	            				setBlock(x,y,Level.HILL_LEFT);
	            			}
            			}
	            		else if (x == xo +length-1){
	            			if(y == floor-6){
		            			setBlock(x,y,Level.HILL_TOP_RIGHT);
		            		}
	            			else if(y > floor-6){
	            				setBlock(x,y,Level.HILL_RIGHT);
	            			}
	            		}
	            		else{
	            			if(y == floor-6){
		            			setBlock(x,y,Level.HILL_TOP);
		            		}
	            			else if(y > floor-6){
	            				setBlock(x,y,Level.HILL_FILL);
	            			}
	            		}
	            		
	            		
	            	}
	                if (y >= floor) {
	                    setBlock(x, y, Level.GROUND);
	                }
	            }
	        }
	        return length;
    	
	    }
	       	  
	  private int buildKillerSpecial(int xo,  int maxLength, boolean safe) {
	    	System.out.println("Building Custom at " + xo);
	    	custom_piece++;
		  
		  int length = 10 + random.nextInt(5);

	        if (length > maxLength)
	            length = maxLength;

	        int floor = height - 2 - random.nextInt(3);

	        //runs from the specified x position to the length of the segment
	        for (int x = xo; x < xo + length; x++) {

	            for (int y = 0; y < height; y++) {
	                if (x <= xo+(length/2)){
		            	if (y >= floor - 1) {  //this direction confuses me...may end up being wrong
		                    setBlock(x, y, Level.GROUND);
		                }
		            	else if(y== floor-2 && x == (xo+(length/2))-1){
	            			setSpriteTemplate(x, y, new SpriteTemplate(SpriteTemplate.RED_TURTLE, false));
		            	}
	                }
	                else{
	                	if(y>=floor+1){
	                		setBlock(x,y, Level.GROUND);
	                	}
	                	if(y == floor){
	                		setSpriteTemplate(x, y, new SpriteTemplate(SpriteTemplate.GOOMPA, false));
	                	}
	                	
	                }
	            }
	   
	        }
	        return length;	    	
	    }
	  
	  

}
